// 每次调用一个函数都会导致该函数对应的 栈帧 压入栈顶
// 当函数内部执行到 return 或 遇到 throw 就会导致函数立即结束
// 函数结束意味着该函数对应的 栈帧 会从 栈 中弹出
// 函数内未遭遇return和throw但函数中所有代码执行完毕，函数正常结束

function test1(){
    console.log( 'test1' );
}

function test2(){
    console.log( 'test2 : before' );
    test1();
    console.log( 'test2 : after' );
}

function test3(){
    console.log( 'test3 : before' );
    test2();
    console.log( 'test3 : after' );
}

test3();

console.log( '- - - - - - - - - - - - - - - -');

function foo( i ){
    if( i < 0 ){
        return ;
    }
    console.log('begin:' + i);
    foo( i - 1 );
    console.log('end:' + i);
}

foo( 3 ); // foo(3) -> foo(2) --> foo(1) --> foo(0) --> foo(-1)